/////////////////////////////////////////////////////////////////////////////////////////////
///<Filename>  		EventSystem.h		<\Filename>
///<Date>      		09/26/2011			<\Date>      
///<Author>   		Richard A. Small	<\Author>   
///<Purpose>   	
///		To allow the passage of events from system to system
///<\Purpose>
/////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#include "../Main/Standard.h"
#include "../Utility/Utility.h"
#include "../Factory/StackFactory.h"
#include "../Container/Dictionary.h"
#include "../Container/DynamicArray.h"

#include "IEvent.h"
#include "IEventListener.h"

class EventSystem
{
	enum { NUM_OF_EVENT_QUEUES = 2 };

	/// <Summary>	Defines an alias representing the event register map </Summary>
	typedef Dictionary<unsigned long, void*> EventRegisterMap;

	/// <Summary>	Defines an alias representing the listener map </Summary>
	typedef Dictionary<unsigned long, SLList<IEventListener*>> ListenerMap;

	/// <Summary>	Defines an alias representing list of events </Summary>
	typedef DynamicArray<IEvent*> EventList;

	/// <Summary> The registered events that this system can handle </Summary>
	EventRegisterMap registeredEvents;

	/// <Summary> The current events in the queue </Summary>
	EventList currentEvents[2];

	/// <Summary> The current listeners for events </Summary>
	ListenerMap currentListeners;

	/// <Summary> The current list being used </Summary>
	int currentList;

private:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Makes copy and assignment unavailable to this class </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	DISALLOW_COPY_AND_ASSIGN(EventSystem)

	EventSystem(void) 
		: currentList(0),
		registeredEvents(Utility::HashFuncULong),
		currentListeners(Utility::HashFuncULong) { }

	~EventSystem(void) { }

	/// <Summary> The instance of this class </Summary>
	static EventSystem* instance;

public:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Gets the instance of this class </Summary>
	///
	/// <Return>	The instance of this class </Return>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	static EventSystem& GetInstance();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Deletes the instance of this class </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	static void DeleteInstance();

public:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Registers the event described by eventID </Summary>
	///
	/// <Param name="eventID">	Identifier for the event </Param>
	/// 
	/// <Remarks>	
	/// 	This function must be called on all events before that event may be used 
	/// </Remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	void RegisterEvent(const HashString& eventID)
	{
		registeredEvents.Insert(eventID.GetIdentity(), new StackFactory<T>);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Gets an event </Summary>
	///
	/// <Param name="eventID">	Identifier for the event </Param>
	///
	/// <Return>	The event </Return>
	/// 
	/// <Remarks>	
	/// 	This function should always be used to fill out an event instead of using the new keyword
	/// </Remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	T* GetEvent(const HashString& eventID)
	{
		return ((StackFactory<T>*)registeredEvents[eventID.GetIdentity()])->NewObject();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Initializes the event manager </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void Initialize();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Shuts down the event manager </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void Shutdown();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Registers a new listener to the event system </Summary>
	///
	/// <Param name="listener">	The listener to be added </Param>
	/// <Param name="eventID"> 	Identifier for the event to be listened for  </Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void RegisterClient(IEventListener* listener, const HashString& eventID);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Unregister a listener from the event system </Summary>
	///
	/// <Param name="listener">	The listener to be removed </Param>
	/// <Param name="eventID"> 	Identifier for the event to stop listening for </Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void UnRegisterClient(IEventListener* listener, const HashString& eventID);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Unregister all listeners for all events </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void UnRegisterAll();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Sends an event </Summary>
	///
	/// <Param name="eventData">	The event to be processed	</Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void SendEvent(IEvent* eventData);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	
	/// 	Process all current events and sends them out to all listeners expecting them
	/// </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void Update(float delta);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Clears all events from the current queue </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void ClearEvents(void);
};